#include <stdio.h>
#include <gwbase/base/test_harness.h>
#include <gwbase/base/logging.h>
#include <gwbase/base/util.h>
#include <gwlib/gwlib.h>

using namespace dicom;


static int WriteToFile(const void* buf, size_t n, void* data) {
  FILE* f = static_cast<FILE*>(data);
  size_t ret = fwrite(buf, 1U, n, f);
  if (ferror(f))
    return -1;
  return static_cast<int>(ret);
}

static int ReadFromFile(void* buf, size_t n, void* data) {
  FILE* f = static_cast<FILE*>(data);
  size_t ret = fread(buf, 1U, n, f);
  if (ferror(f))
    return -1;
  return static_cast<int>(ret);
}

static int ListItems(GwToken* t, const std::string& id) {
  dicom::DirEntryList items1;
  int ret = dicom::ReadDir(t, "0", &items1);
  if (ret != 0) {
    LOG_ERROR << "readdir failed";
    return -1;
  } else {
    for (size_t i = 0; i < items1.size(); ++i)
      LOG_INFO << "dirent item: " << items1[i].id;
  }
  return 0;
}

static int PrintStatInfo(GwToken* t) {
  dicom::GwSpaceInfo info;
  int ret = dicom::GetGwSpaceInfo(t, &info);
  if (ret != 0) {
    LOG_ERROR << "get space info failed";
    exit(-1);
  } else {
    LOG_INFO << "TOTAL SPACE: " << info.allSpace;
    LOG_INFO << "USED SPACE: " << info.usedSpace;
    LOG_INFO << "CACHE TOTAL SPACE: " << info.cacheTotalSpace;
    LOG_INFO << "CACHE USED SPACE: " << info.cacheUsedSpace;
  }
  return 0;
}

static int do_log(const char* str) {
  return printf("%s\n", str);
}

int main(int argc, char** argv)
{
  int ret;

  dicom::InitGwLib();
  dicom::SetGwLogcallback(do_log);

  LOG_ERROR << "Log test";

  const char* src_file = "test.dat";
  system("echo a111111 >> test.dat");
  GwToken* t = NULL; 

  std::string server = "http://127.0.0.1:3322";
  std::string user = "surdoctesthC7iiexF@cc.cc";
  std::string pwd = "aA111111";
  std::string ukey_id = "UKEY_ID";

  if (argc >= 3) {
    user = argv[1];
    pwd = argv[2];
  }

  if (argc >= 4)
    server = argv[3];

  ret = dicom::Login(server, user, pwd, ukey_id, &t);
  if (ret != 0) {
    LOG_ERROR << "login failed";
    return -1;
  }

  PrintStatInfo(t);

  DirEntry test_folder;
  ret = dicom::CreateDir(t, "0", "test_folder", &test_folder);
  if (ret != 0) {
    ret = dicom::Lookup(t, "0", "test_folder", &test_folder);
  }
  if (ret != 0) {
    LOG_ERROR << "failed to create test folder";
    return -1;
  }

  std::string test_folder_id = test_folder.id;

  if (ListItems(t, test_folder_id) != 0)
    return -1;

  DirEntry result;
  FileInfo info;
  dicom::StatPath(src_file, &info);
  FILE* f1 = fopen(src_file, "rb");
  ret = dicom::UploadFile(t, test_folder_id,
                          src_file, 
                          info.size, 
                          info.mtime.ToTimeT(), 
                          ReadFromFile, 
                          f1,
                          &result);
  if (ret != 0) {
    LOG_ERROR << "upload failed";
    return -1;
  } 
  fclose(f1);

  if (result.size != info.size) {
    LOG_ERROR << "server return wrong file size after uploading (1)";
    return -1;
  }

  DirEntry de;
  ret = dicom::Lookup(t, test_folder_id, src_file, &de);
  if (ret != 0) {
    LOG_ERROR << "lookup failed";
    return -1;
  } else {
    LOG_INFO << "dirent: " << de.id;
  }
  
  if (de.size != info.size) {
    LOG_ERROR << "server return wrong file size after uploading (2)";
    return -1;
  }

  if (ListItems(t, test_folder_id) != 0)
    return -1;

  printf("Press enter to download file...\n");
  getchar();

  const char* dest_file = "download.dat";
  FILE* f = fopen(dest_file, "wb");
  ret = dicom::DownloadFile(t, result.id, WriteToFile, f);
  if (ret != 0) {
    LOG_ERROR << "download failed";
    return -1;
  } 

  fclose(f);
  printf("Press enter to rename file...\n");
  ::unlink(dest_file);

  ret = dicom::RenameFile(t, de.id, test_folder_id, "new_name.txt");
  if (ret != 0) {
    LOG_ERROR << "Failed to rename file";
  }

  ret = dicom::DeleteFile(t, de.id);
  if (ret != 0) {
    LOG_ERROR << "delete file failed";
    return -1;
  } 

  ret = dicom::DeleteDir(t, test_folder_id);
  if (ret != 0) {
    LOG_ERROR << "delete folder failed";
    return -1;
  } 

  dicom::LogoutAndReleaseToken(t);
  t = NULL;
  return 0;
}
